package com.gcosc.hly.codejava.service.impl;

import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.gcosc.hly.codejava.Final.PaperFinal;
import com.gcosc.hly.codejava.Utils.DateUtils;
import com.gcosc.hly.codejava.Utils.RequestContextUtils;
import com.gcosc.hly.codejava.VO.*;
import com.gcosc.hly.codejava.entity.Answer;
import com.gcosc.hly.codejava.entity.AnswerItem;
import com.gcosc.hly.codejava.entity.Paper;
import com.gcosc.hly.codejava.entity.Topic;
import com.gcosc.hly.codejava.mapper.PaperMapper;
import com.gcosc.hly.codejava.params.QueryParams;
import com.gcosc.hly.codejava.result.Code;
import com.gcosc.hly.codejava.service.AnswerItemService;
import com.gcosc.hly.codejava.service.AnswerService;
import com.gcosc.hly.codejava.service.PaperService;
import com.gcosc.hly.codejava.service.TopicService;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;

import java.util.*;

@Service
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class PaperServiceImpl extends ServiceImpl<PaperMapper, Paper> implements PaperService {

    private final TopicService topicService;
    private final AnswerService answerService;
    private final AnswerItemService answerItemService;

    // 检查更新次数
    int i = 0;

    @Override
    public Page<Paper> queryParams(QueryParams params) {
        Page<Paper> page = lambdaQuery()
                .eq(StringUtils.isNotBlank(params.getIds()), Paper::getId, params.getIds())
                .eq(params.getStatus() != 0, Paper::getStatus, params.getStatus())
                .eq(StringUtils.isNotBlank(params.getType()), Paper::getType, params.getType())
                .like(StringUtils.isNotBlank(params.getTitle()), Paper::getTitle, params.getTitle())
                .eq(params.getTime() != 0, Paper::getTime, params.getTime())
                .orderByDesc(Paper::getCreateTime)
                .page(new Page<Paper>(params.getPage(), params.getLimit()));
        return page;
    }

    @Override
    public PaperVO detail(int id) {
        int uid = RequestContextUtils.getUser().getId();
        Paper paper = getById(id);

        long time = System.currentTimeMillis();

        // 时段和任务试卷判断是否在时段内
        if (PaperFinal.TIME.equals(paper.getType()) || PaperFinal.TASK.equals(paper.getType())){
            long start = paper.getStartTime().getTime();
            long end = paper.getEndTime().getTime();
            if (time < start) Code.BUSINESS_ERR("试卷未开始");
            if (time > end) Code.BUSINESS_ERR("试卷已结束");
        }

        Answer answer = answerService.lambdaQuery()
                .eq(Answer::getUid, uid)
                .eq(Answer::getPaperId, id)
                .orderByDesc(Answer::getId)
                .last("limit 1")
                .one();

        List<TopicVO> topics = topicService.getTopicVOList(QueryParams.builder()
                .ids(paper.getList())
                .build());

        // 若做过该试卷
        if (answer != null){
            //是否为任务试卷
            if (PaperFinal.TASK.equals(paper.getType()) && answer.getStatus() != 0){
                Code.BUSINESS_ERR("任务已完成");
            }
            //答题卡未提交
            if (answer.getStatus() == 0){
                // 答题卡超时
                if (answer.getCreateTime().getTime() +
                        paper.getTime() < time){
                    answer.setStatus(2);
                    answer.setSubmitTime(new Date(answer.getCreateTime().getTime() + paper.getTime()));
                    answerService.edit(answer);
                    answer = null;
                }else{
                    // 答题卡没有超时，获取之前答题记录
                    for (TopicVO topic : topics) {
                        topic.setAnswerItem(answerItemService.lambdaQuery()
                                .eq(AnswerItem::getUid, uid)
                                .eq(AnswerItem::getTopicId, topic.getTopic().getId())
                                .eq(AnswerItem::getAnswerId, answer.getId())
                                .one());
                    }
                }
            }else {
                answer = null;
            }
        }

        return PaperVO.builder().list(topics).paper(paper).answer(answer).build();
    }

    /**
     *  检查试卷是否超时
     * */
    @Scheduled(cron ="0 */5 * * * ?")
    public void checkAnswerTimeout(){


        i++;

        long time = System.currentTimeMillis();

        List<Answer> list = answerService.lambdaQuery()
                .eq(Answer::getStatus, 0)
                .list();

        for (Answer answer : list) {
            Paper paper = getById(answer.getPaperId());

            if (answer.getCreateTime().getTime() +
                    paper.getTime() < time){

                answer.setStatus(2);
                answer.setSubmitTime(new Date(answer.getCreateTime().getTime() + paper.getTime()));
                answerService.edit(answer);
            }
        }

        System.out.println("检查答题卡是否超时已完成，执行" + i + "次================" + DateUtils.dateToString("YYYY-MM-dd HH:mm:ss") + "=========================================");

    }

    @Override
    public int edit(Paper paper) {

        if (paper.getList() == null){
            Code.BUSINESS_ERR("题目列表不能为空");
        }

        Integer count = Arrays.stream(paper.getList().split(","))
                .map(i -> Integer.valueOf(i)).reduce(0, (m, n) ->
                        m + topicService.getById(n).getLevel());

        paper.setCountFraction(count);

        saveOrUpdate(paper);
        return paper.getId();
    }

    @Override
    public PaperStatisticsVO getStatisticsById(String id) {
        Paper paper = getById(id);

        ArrayList<Long> lineValues = new ArrayList<>();
        ArrayList<String> lineNames = new ArrayList<>();


        // 30天曲线图
        for (int i = 29; i >= 0; i--) {

            Date dateMin = DateUtils.ZeroTime(0, 0, i);
            Date dataMax = DateUtils.ZeroTime(0, 0, i - 1);

            Long count = answerService.lambdaQuery()
                    .eq(Answer::getPaperId, paper.getId())
                    .lt(Answer::getCreateTime, dataMax)
                    .gt(Answer::getCreateTime, dateMin)
                    .count();

            lineValues.add(count);

            lineNames.add(DateUtils.dateToString(dateMin, "MM/dd"));
        }

        // 柱状图
        ArrayList<Long> barValues = new ArrayList(){{
            add(answerService.lambdaQuery()
                    .eq(Answer::getPaperId, paper.getId())
                    .eq(Answer::getStatus, 0)
                    .count());
            add(answerService.lambdaQuery()
                    .eq(Answer::getPaperId, paper.getId())
                    .eq(Answer::getStatus, 1)
                    .count());
            add(answerService.lambdaQuery()
                    .eq(Answer::getPaperId, paper.getId())
                    .eq(Answer::getStatus, 2)
                    .count());
        }};


        // 饼图
        ArrayList<String> barNames = new ArrayList(){{
            add("未提交");
            add("已提交");
            add("已超时");
        }};

        ArrayList<HashMap<String, String>> pie = new ArrayList(){{
            add(new HashMap(){{
                put("name", "未提交");
                put("value", answerService.lambdaQuery()
                        .eq(Answer::getPaperId, paper.getId())
                        .eq(Answer::getStatus, 0)
                        .count());
            }});
            add(new HashMap(){{
                put("name", "已提交");
                put("value", answerService.lambdaQuery()
                        .eq(Answer::getPaperId, paper.getId())
                        .eq(Answer::getStatus, 1)
                        .count());
            }});
            add(new HashMap(){{
                put("name", "已超时");
                put("value", answerService.lambdaQuery()
                        .eq(Answer::getPaperId, paper.getId())
                        .eq(Answer::getStatus, 2)
                        .count());
            }});
        }};

        ArrayList<HashMap<String, String>> counts = new ArrayList<>(){{


            add(new HashMap(){{
                put("title", "新增答题数");
                put("count", lineValues.get(lineValues.size() - 1));
            }});

            Answer answer = answerService
                    .lambdaQuery()
                    .eq(Answer::getPaperId, paper.getId())
                    .select(Answer::getFraction)
                    .orderByDesc(Answer::getFraction)
                    .last("limit 1")
                    .one();


            add(new HashMap(){{
                put("title", "最高分");
                put("count", answer == null ? 0 : answer.getFraction());
            }});


            add(new HashMap(){{
                put("title", "累计提交总数");
                put("count", answerService
                        .lambdaQuery()
                        .eq(Answer::getPaperId, paper.getId())
                        .count());
            }});

            // 统计总分
            add(new HashMap(){{
                int countFraction = 0;
                for (Answer answer : answerService
                        .lambdaQuery()
                        .eq(Answer::getPaperId, paper.getId())
                        .select(Answer::getFraction)
                        .orderByDesc(Answer::getFraction)
                        .list()) {
                    countFraction += answer.getFraction();
                }
                put("title", "累计总分");
                put("count", countFraction);
            }});
        }};


        Integer level = 0;
        for (Topic topic : topicService.lambdaQuery()
                .in(Topic::getId, paper.getList().split(","))
                .select(Topic::getLevel)
                .list()) {
            level += topic.getLevel();
        }

        return PaperStatisticsVO.builder()
                .lineValues(lineValues)
                .counts(counts)
                .pie(pie)
                .level(level)
                .barNames(barNames)
                .barValues(barValues)
                .lineNames(lineNames)
                .paper(paper).build();
    }

}
